import type { MapOption, DrawType } from './types'
import 'ol/ol.css'
import Map from 'ol/Map'
import { get as getProjection, fromLonLat } from 'ol/proj'
import { getTopLeft, getWidth } from 'ol/extent'
import TileLayer from 'ol/layer/Tile'
import WMTS from 'ol/source/WMTS'
import WMTSTileGrid from 'ol/tilegrid/WMTS'
import View from 'ol/View'
import { defaults as defaultControls, MousePosition, ScaleLine, Rotate } from 'ol/control'
import { createStringXY } from 'ol/coordinate'
import Overlay from 'ol/Overlay'
import VectorSource from 'ol/source/Vector'
import VectorLayer from 'ol/layer/Vector'
import { Circle as CircleStyle, Fill, Icon, Stroke, Style, Text } from 'ol/style.js'
import GeoJSON from 'ol/format/GeoJSON.js'

import Polyline from 'ol/format/Polyline.js'
import Feature from 'ol/Feature.js'
import Point from 'ol/geom/Point.js'
import { getVectorContext } from 'ol/render.js'
import LineString from 'ol/geom/LineString.js'

import Draw from 'ol/interaction/Draw'
import Modify from 'ol/interaction/Modify'
import Snap from 'ol/interaction/Snap'

import * as turf from '@turf/turf'

import Polygon from 'ol/geom/Polygon'

import zdjrImg from '@/assets/images/战斗机-red.png'
import zdjbImg from '@/assets/images/战斗机-blue.png'
import kjrImg from '@/assets/images/客机-red.png'
import kjbImg from '@/assets/images/客机-blue.png'
import air_defense_identification_zone from '@/public/json/mapJson/air_defense_identification_zone.json'
import { mapData } from '@/store/modules/mapData'

const WMTSURL_VEC = 'http://t{0-7}.tianditu.gov.cn/vec_c/wmts?tk='
const WMTSURL_IMG = 'http://t{0-7}.tianditu.gov.cn/img_c/wmts?tk='
const WMTSURL_CVA = 'http://t{0-7}.tianditu.gov.cn/cva_c/wmts?tk='

const WEBKEY = '337d62ff67c603abe0f6fce62e4752a4'

const mapDatas = mapData()
const sign = {
  VEC: 'vec',
  IMG: 'img',
  CVA: 'cva'
} as const

class MapUtils {
  map: Map
  view?: View
  popup?: Overlay
  mousePositionControl?: MousePosition
  ScaleLineControl?: ScaleLine
  tempSource: any
  tempLayer: VectorLayer<any> // 飞行轨迹
  vecLayer?: TileLayer<WMTS>
  imgLayer?: TileLayer<WMTS>
  drawLayers: {
    [id: string]: VectorLayer<any>
  } = {}
  textLayers: {
    [id: string]: VectorLayer<any>
  } = {}
  cvaLayer?: TileLayer<WMTS>
  focusTarget?: any
  draw: Draw
  drawLayer
  snap
  modify
  randomNumber

  // 是否已经渲染成功
  isAreadyInit?: boolean

  constructor() {
    this.tempLayer = new VectorLayer({
      source: new VectorSource()
    })
    this.map = undefined as Map
    this.draw = new Draw({
      type: 'Polygon'
    })

    this.randomNumber = {
      speed: 0,
      altitude: 0
    }
  }

  initMap(options: MapOption) {
    const layersOption: Array<TileLayer<WMTS> | VectorLayer<any>> = [] // 初始化图层
    const EPSG = 'EPSG:4326'

    // 矢量底图
    const vecUrl = WMTSURL_VEC + WEBKEY
    this.vecLayer = this.initWMTSTile(vecUrl, sign.VEC, EPSG)

    // 卫星地图
    const imgUrl = WMTSURL_IMG + WEBKEY
    this.imgLayer = this.initWMTSTile(imgUrl, sign.IMG, EPSG)
    this.imgLayer.setVisible(false)

    // 矢量注记
    const cvaUrl = WMTSURL_CVA + WEBKEY
    this.cvaLayer = this.initWMTSTile(cvaUrl, sign.CVA, EPSG)

    // 东海防空图
    const defenseZoneFeatures: any = new VectorSource({
      features: new GeoJSON().readFeatures(air_defense_identification_zone)
    })

    const defenseZoneLayer = new VectorLayer({
      className: 'defense-zone',
      source: defenseZoneFeatures
    })

    layersOption.push(this.vecLayer, this.imgLayer, this.cvaLayer, this.tempLayer, defenseZoneLayer)

    // 视图层
    this.view = new View({
      center: options.center,
      // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
      projection: getProjection(EPSG)!,
      zoom: options.zoom,
      minZoom: options.minZoom,
      maxZoom: options.maxZoom
    })

    // 显示鼠标光标位置坐标的控件
    this.mousePositionControl = new MousePosition({
      coordinateFormat: createStringXY(4), // 保留四位小数
      projection: EPSG,
      className: 'custom-mouse-position',
      target: 'mouse-position'
    })

    // 比例尺
    this.ScaleLineControl = new ScaleLine({
      units: 'metric',
      minWidth: 80
    })

    // 初始地图弹窗
    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
    const container = document.getElementById('popup')!
    this.popup = new Overlay({
      element: container,
      autoPan: {
        animation: {
          duration: 250
        }
      } // 地图定位时平滑移动
    })

    // 地图实例
    this.map = new Map({
      controls: defaultControls({
        attribution: true, // 图层筛选
        rotate: true,
        zoom: true
      }),
      layers: layersOption,
      overlays: [this.popup],
      target: options.target,
      view: this.view
    })

    // 添加控件
    this.map.addControl(this.mousePositionControl)
    this.map.addControl(this.ScaleLineControl)

    this.isAreadyInit = true

    return this.map
  }

  backToHome(options: MapOption) {
    this.map.getView().setCenter(options.center)
    this.map.getView().setZoom(options.zoom)
  }

  // 渲染基础地图瓦片图层
  initWMTSTile(url: string, type: string, epsg: string, style?: string | undefined) {
    const projection = getProjection(epsg) // 获取当前底图类型的对象
    const projectionExtent = projection?.getExtent()!
    const size = getWidth(projectionExtent) / 256
    const resolutions = new Array(18)
    const matrixIds = new Array(18)
    for (let z = 1; z < 19; ++z) {
      resolutions[z] = size / Math.pow(2, z)
      matrixIds[z] = z
    }

    return new TileLayer({
      opacity: 1,
      source: new WMTS({
        url,
        layer: type,
        matrixSet: 'c',
        format: 'tiles',
        style: style || 'default',
        projection: projection!,
        tileGrid: new WMTSTileGrid({
          origin: getTopLeft(projectionExtent),
          resolutions,
          matrixIds
        }),
        wrapX: true
      })
    })
  }
  // 加载离线地图
  initOfflineTile(path: string) {
    const url = `${path}/{z}/{x}/{y}.png`
    return new TileLayer({
      source: new ol.source.XYZ({
        url: 'file:///path/to/your/local/tiles/{z}/{x}/{y}.png'
      })
    })
  }
  /**
   * 定位到区域
   * @param location
   */
  moveToArea(id: string) {
    // const extent = getExtent(points);
    console.log(this.drawLayers)

    this.drawLayers[id] && this.map.getView().fit(this.drawLayers[id].getSource().getExtent())
    // this.map.getView().fit(points);
  }
  /**
   * 跟踪目标,根据经纬度地图自动移动
   */

  moveToPoint(location: number[]) {
    this.map.getView().animate({
      center: location,
      duration: 0 // 动画持续时间，单位为毫秒
    })
  }
  /**
   * 飞机动画
   * @param jsonData 飞机航线 json 数据
   * @param speed 飞机速度 km/h
   */

  initFlightAnimation(jsonData: any, speed = 20, moveCb?: any) {
    if (!jsonData) {
      throw new Error('init flight animation must need a geoJson data, it is better for LineString')
    }

    const { route } = getPlaneFlyRoute(jsonData) // 轨迹要素

    const resultJsonData = new GeoJSON().readFeatures(jsonData)[0] // json 数据对象

    // @ts-ignore
    const coordinates_demo = resultJsonData.getGeometry().getCoordinates()
    let azimuth = bearing(coordinates_demo)

    const startMarker: any = new Feature({
      type: 'icon',
      geometry: new Point(route.getFirstCoordinate())
    })
    const airId = `air_${resultJsonData.getProperties().modelType}`
    // const textId = `text_${resultJsonData.getProperties().modelType}`;
    const textContent = `${resultJsonData.getProperties().modelType}\n`
    const pointText = this.renderText(
      route.getFirstCoordinate(),
      `text_${resultJsonData.getProperties().modelType}`,
      textContent
    )
    const position = startMarker.getGeometry().clone()
    const geoMarker = new Feature({
      type: 'geoMarker',
      id: `air_${resultJsonData.getProperties().modelType}`,
      name: `air_${resultJsonData.getProperties().modelType}`,
      geometry: position,
      zIndex: 19
    })

    // 添加 属性数据
    geoMarker.setProperties({
      properties: resultJsonData.getProperties()
    })
    const flyIcon = new Icon({
      anchor: [0.5, 1],
      src: this.renderIcon(resultJsonData.getProperties()),
      rotateWithView: true,
      rotation: azimuth,
      scale: 0.1
    })
    const styles = {
      route: new Style({
        stroke: new Stroke({
          width: 6,
          color: [237, 212, 0, 0.8]
        })
      }),
      icon: new Style({
        image: new CircleStyle({
          radius: 7,
          fill: new Fill({ color: 'black' }),
          stroke: new Stroke({
            color: 'white',
            width: 2
          })
        })
      }),
      geoMarker: new Style({
        image: flyIcon
      })
    }

    const vectorLayer = new VectorLayer({
      source: new VectorSource({
        features: [geoMarker]
      }),
      style(feature) {
        return styles[feature.get('type')]
      }
    })

    this.map?.addLayer(vectorLayer)

    let distance = 0
    let lastTime
    let planeFlyTrackFeature
    // 记录飞机当前位置
    let currentCoordinate = route.getCoordinateAt(distance)

    // const _runTask = (task) => {
    //   requestIdleCallback((deadline) => {
    //     if(deadline.timeRemaining() > 0) {
    //       task()
    //     } else {
    //       _runTask(task)
    //     }
    //   })
    // }

    /**
     *  动态变化：飞行动画
     * @param event
     */
    const moveFeature = (event) => {
      /* 飞机动画画线 */
      const planePoint = geoMarker.getGeometry().getCoordinates()
      const preCoordinate = [...currentCoordinate]
      planeFlyTrackFeature = this.drawLineByCoordinates([planePoint, preCoordinate], '#3D3C3C')

      this.tempLayer.getSource().addFeature(planeFlyTrackFeature)
      /* 飞机动画画线 */

      const { time } = event.frameState
      const elapsedTime = time - lastTime
      distance = (distance + (speed * elapsedTime) / 1e6) % 2
      lastTime = time

      // 当前飞机位置
      currentCoordinate = route.getCoordinateAt(distance)
      azimuth = bearing([preCoordinate, currentCoordinate])

      const rotate = turf.radiansToDegrees(azimuth)

      this.randomChangeSpeedAndAltitude('speed', resultJsonData.getProperties()?.maxSpeed)
      this.randomChangeSpeedAndAltitude('altitude', resultJsonData.getProperties()?.maxAltitude)

      moveCb &&
        moveCb(geoMarker.get('id'), currentCoordinate, getdesiredTrack([planePoint, route.getFirstCoordinate()]), {
          ...Object.assign(resultJsonData.getProperties(), {
            speed: this.randomNumber.speed,
            height: this.randomNumber.altitude
          }),
          rotate
        })
      const planeInfo = mapDatas.getplaneInfo(airId)

      // 设置飞机位置及方向
      flyIcon.setRotation(azimuth)

      position.setCoordinates(currentCoordinate)
      // 设置文字位置
      pointText.point.setCoordinates(currentCoordinate)
      const textRes = `
      ${resultJsonData.getProperties().modelType}
      ${planeInfo.desiredTrack}
      ${planeInfo.speed}km/h
      ${planeInfo.height}m
      `
      // 设置文字内容
      pointText.text.setText(textRes)

      const vectorContext = getVectorContext(event)
      vectorContext.setStyle(styles.geoMarker)
      vectorContext.drawGeometry(position)
      this.map?.render()

      // 重新从起点开始飞行
      if (distance > 1) {
        distance = 0
        if (planeFlyTrackFeature) {
          this.tempLayer.getSource().clear() // 清除轨迹
        }
      }
    }

    function startAnimation() {
      lastTime = Date.now()
      vectorLayer.on('postrender', moveFeature)
    }

    startAnimation()
  }

  // 图标生成
  renderIcon(properties: any) {
    if (!properties) {
      return zdjrImg
    }
    if (properties.linkData === 'airplaneData') {
      if (properties.country === '中国') {
        return zdjrImg
      } else {
        return zdjbImg
      }
    }
    if (properties.linkData === 'passengerPlaneData') {
      if (properties.country === '中国') {
        return kjrImg
      } else {
        return kjbImg
      }
    }
  }

  renderText(position: number[], id: string, textContent: string) {
    // 定义标注的位置

    // 创建点
    const point = new Point(position)
    // 创建特性
    const feature = new Feature(point)

    // 创建文本样式
    const text = new Text({
      font: 'bold 11px "Open Sans", "Arial Unicode MS", "sans-serif"',
      text: textContent,
      offsetX: 25,
      offsetY: 25,
      placement: 'line',
      fill: new Fill({
        color: 'black'
      })
    })
    // 创建样式
    const style = new Style({
      text
    })

    // 设置特性的样式
    feature.setStyle(style)

    // 创建矢量源
    const source = new VectorSource({
      features: [feature]
    })

    // 创建矢量图层
    const layer = new VectorLayer({
      source
    })
    this.textLayers[id] = layer
    // 添加图层到地图
    this.map.addLayer(layer)
    return {
      point,
      text
    }
  }

  /**
   * 通过两点画线
   * @param coordinates 经纬度数据
   * @param color 颜色
   * @returns Feature
   */
  drawLineByCoordinates(coordinates: Array<number[]>, color: string): Feature {
    const lineFeature = new Feature({
      geometry: new LineString(coordinates)
    })

    // 创建线样式
    const lineStyle = new Style({
      stroke: new Stroke({
        width: 1,
        color
      })
    })

    // 样式添加进因素中
    lineFeature.setStyle(lineStyle)

    return lineFeature
  }

  /**
   * 绘制图形要素
   * @param measureType 绘制的类型
   */
  drawArea(measureType: DrawType) {
    this.map.removeInteraction(this.draw)
    this.map.removeLayer(this.drawLayer)

    const source = new VectorSource({ wrapX: false })

    this.drawLayer = new VectorLayer({
      source,
      style: new Style({
        fill: new Fill({
          color: 'rgba(255, 255, 255, 0.1)'
        }),
        stroke: new Stroke({
          color: 'red',
          width: 1
        })
      })
    })

    this.map.addLayer(this.drawLayer)

    this.draw = new Draw({
      stopClick: true,
      source,
      type: measureType
    })

    this.map.addInteraction(this.draw)

    this.modifyArea()
  }

  /**
   * 绘制多边形
   */
  drawPolygon(id: string, points: number[]) {
    // 将经纬度坐标转换为OpenLayers的投影
    // const coordinates = pointss.map((point) => fromLonLat([point[0], point[1]]));
    // coordinates.push(coordinates[0]);
    points.push(points[0])
    // 创建多边形
    const polygon = new Polygon([points])

    // 创建特性
    const feature = new Feature({
      geometry: polygon
    })

    // 创建矢量源
    const source = new VectorSource()
    source.addFeature(feature)
    // 创建矢量图层
    const veclayer = new VectorLayer({
      source,
      zIndex: 9999,
      style: new Style({
        fill: new Fill({
          color: 'rgba(255, 255, 255, 0.1)'
        }),
        stroke: new Stroke({
          color: 'red',
          width: 1
        })
      })
    })
    //  const defenseZoneFeatures: any = new VectorSource({
    //   features: new GeoJSON().readFeatures(air_defense_identification_zone),
    // });

    // const defenseZoneLayer = new VectorLayer({
    //   className: 'defense-zone',
    //   source: defenseZoneFeatures,
    // });
    this.drawLayers[id] = veclayer
    // 添加图层到地图
    this.map.addLayer(veclayer)
  }

  /**
   * 编辑图形要素
   * @param feature
   */
  modifyArea() {
    this.modify = new Modify({ source: this.drawLayer.getSource() })
    this.map.addInteraction(this.modify)

    this.snap = new Snap({ source: this.drawLayer.getSource() })
    this.map.addInteraction(this.snap)

    this.draw.on('drawend', (evt) => {
      console.log('drawend', evt)
      this.drawLayers.temp = this.drawLayer
      this.map.removeInteraction(this.draw)
    })
    this.modify.on('modifyend', (evt) => {
      console.log('%c [ evt ]-400', 'font-size:13px; background:#1e25b1; color:#6269f5;', evt)
    })
  }

  /**
   * 手动移除图层和取消绘制
   * @param layerName 图层名称
   */
  handleRemoveLayer(layerName: string) {
    this.map.removeInteraction(this.draw)
    this.map.removeLayer(this[layerName])
  }

  // 添加弹窗覆盖物
  /**
   * @param idName dom id 名
   * @param clickCoordinate 坐标点point[lng, lat]
   */
  addPopup(idName: string, clickCoordinate: number[] | undefined) {
    this.popup = new Overlay({
      element: document.getElementById(idName)!,
      autoPan: {
        animation: {
          duration: 250
        }
      } // 地图定位时平滑移动
    })
    this.popup?.setPosition(clickCoordinate)
    if (this.popup) {
      this.map?.addOverlay(this.popup)
    }
  }

  /**
   * 清除弹窗覆盖物
   */
  removePopupOverlay() {
    const res = this.map?.getOverlays()
    console.log('%c [ res ]-192', 'font-size:13px; background:#d6263c; color:#ff6a80;', res)
    if (!this.popup) {
      return
    }
    this.map?.removeOverlay(this.popup)
  }

  onDestory() {
    for (const i in this.drawLayers) {
      this.map.removeLayer(this.drawLayers[i])
      this.drawLayers[i].dispose()
    }
    for (const i in this.textLayers) {
      this.map.removeLayer(this.textLayers[i])
      this.textLayers[i].dispose()
    }
    this.map.dispose()
  }

  randomChangeSpeedAndAltitude(type: string, value: number) {
    this.randomNumber[type] = value

    const plusOrMins = Math.random() > 0.5

    if (plusOrMins) {
      this.randomNumber[type] += 10
    } else {
      this.randomNumber[type] -= 10
    }
  }
}

/**
 * 根据 geoJson 数据得到 Polyline
 * @param data geoJson
 * @returns route
 */
const getPlaneFlyRoute = (data: any): any => {
  const geometry = new GeoJSON().readFeatures(data)[0].getGeometry()

  const format = new Polyline({
    factor: 1e6
  })

  const encodedString = format.writeGeometry(geometry, {
    dataProjection: 'EPSG:4326',
    featureProjection: 'EPSG:3857'
  })

  const route = new Polyline({
    factor: 1e6
  }).readGeometry(encodedString, {
    dataProjection: 'EPSG:4326',
    featureProjection: 'EPSG:3857'
  })

  return { route }
}

const getAngle = (coordinates: Array<number[]>): number => {
  if (!coordinates) {
    throw new Error('Latitude and longitude data are needed to calculate the azimuth')
  }

  const data_1 = coordinates[0]
  const data_2 = coordinates[1]

  const angle = turf.rhumbBearing(data_1, data_2)

  return angle
}

/**
 * 计算图片自正北的顺时针旋转角度
 * @param coordinates 两组经纬度数据，二维数组，经度在前
 * @returns radian 弧度
 */
const bearing = (coordinates: Array<number[]>): number => {
  const angle = getAngle(coordinates)

  const radian = turf.degreesToRadians(angle)

  return radian
}

const getdesiredTrack = (coordinates: Array<number[]>): string => {
  if (!coordinates) {
    throw new Error('Latitude and longitude data are needed to calculate the azimuth')
  }

  const angle = getAngle(coordinates)
  // console.log('angle', angle);
  if (angle >= -180 && angle < -90) {
    return `北偏东 ${Math.abs(angle + 180).toFixed(0)}°`
  } else if (angle >= -90 && angle < 0) {
    return `南偏东 ${Math.abs(angle + 90).toFixed(0)}°`
  } else if (angle >= 0 && angle < 90) {
    return `南偏西 ${Math.abs(angle).toFixed(0)}°`
  } else if (angle >= 90 && angle <= 180) {
    return `北偏西 ${Math.abs(angle - 90).toFixed(0)}°`
  } else {
    return 'Invalid angle'
  }
}

export default MapUtils
